package org.testile.datasetgenerifier;

import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import groovy.lang.Script;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.commons.configuration.CompositeConfiguration;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.io.IOUtils;
import org.dbunit.dataset.IDataSet;

/**
 *
 * @author ytoh
 */
public class GenericDataSetFactory {
	private static final String DEFAULT_PROPERTIES_LOCATION = "dataset-generifier-defaults.properties";
	private static final Configuration DEFAULT_CONFIGURATION;

	static {
		DEFAULT_CONFIGURATION = loadConfiguration(DEFAULT_PROPERTIES_LOCATION);
	}

	private final Configuration configuration;

	/**
	 *
	 * @param configuration
	 */
	private GenericDataSetFactory(Configuration configuration) {
		this.configuration = configuration;
	}

	private static Configuration loadConfiguration(String location) {
		try {
			return new PropertiesConfiguration(location);
		} catch (ConfigurationException ex) {
			throw new IllegalStateException("Could not load defaults.", ex);
		}
	}

	/**
	 *
	 * @return
	 */
	public static GenericDataSetFactory newInstance() {
		return new GenericDataSetFactory(DEFAULT_CONFIGURATION);
	}

	/**
	 *
	 * @param propertyFileLocation
	 * @return
	 */
	public static GenericDataSetFactory newInstance(String propertyFileLocation) {
		Configuration overrides = loadConfiguration(propertyFileLocation);
		Configuration composed = new CompositeConfiguration(Arrays.asList(overrides, DEFAULT_CONFIGURATION));
		return new GenericDataSetFactory(composed);
	}

	/**
	 *
	 * @param original
	 * @param generificationScript
	 * @return
	 */
	public GenerifiedDataSet dataSetFrom(IDataSet original, File generificationScript) {
		GroovyShell groovyShell = new GroovyShell();
		Script compiledScript = compileScript(groovyShell, generificationScript);
		Binding binding = new Binding();
		binding.setVariable("context", getInitializedBuilder());
		compiledScript.setBinding(binding);
		DatasetGenerifier dataSetGenerifier = (DatasetGenerifier) compiledScript.run();
		return new GenerifiedDataSet(original, dataSetGenerifier);
	}

	private Script compileScript(GroovyShell groovyShell, File script) {
		String scriptText = loadScript(script);
		return groovyShell.parse("context.with {" + scriptText + "}; result = context.datasetGenerifier");
	}

	private String loadScript(File script) {
		try {
			return IOUtils.toString(new FileInputStream(script));
		} catch (IOException ex) {
			throw new IllegalArgumentException("Could not load generification script from file [" + script + "]", ex);
		}
	}

	private DataSetGenerifierBuilder getInitializedBuilder() {
		Configuration factoriesDefinitions = configuration.subset("trasformation.factory");
		return DataSetGenerifierBuilder.with(loadTransforationFactories(factoriesDefinitions));
	}

	private Map<String, TransformationFactory> loadTransforationFactories(Configuration configuration) {
		Map<String, TransformationFactory> factories = new HashMap<String, TransformationFactory>();

		Iterator keys = configuration.getKeys();
		while (keys.hasNext()) {
			String name = (String) keys.next();
			TransformationFactory factory = InstantiationUtils.getInstance(TransformationFactory.class, configuration.getString(name));
			factories.put(name, factory);
		}

		return factories;
	}
}
